﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using WPCoco.Controllers;
using Microsoft.Xna.Framework.Graphics;
using WPCoco.Transitions;
using System.Diagnostics;
using WPCoco.Audio;
using WPCoco.Graphics;

namespace WPCoco
{
    public partial class Coco : DrawableGameComponent
    {
        #region Singleton

        static Coco shared;

        /// <summary>
        /// Shared instane of Coco Engine
        /// </summary>
        public static Coco Shared
        {
            get { return shared; }
        }

        /// <summary>
        /// First method to be called befor use the engine, use it in "Initialize()" method of your game
        /// </summary>
        /// <param name="game"></param>
        public static void Initialize(Game game) 
        {
            if (shared != null) return;

            shared = new Coco(game);

            PresentationParameters p = Screen.CurrentGraphicsDevice.PresentationParameters.Clone();
            p.RenderTargetUsage = RenderTargetUsage.PreserveContents;
            Screen.CurrentGraphicsDevice.Reset(p);
            
            game.Components.Add(shared);
        }

        GraphicsDeviceManager graphicsManager;

        public GraphicsDeviceManager GraphicsManager
        {
            get { return graphicsManager; }
            private set { graphicsManager = value; }
        }

        /// <summary>
        /// Content manager asosiated to the current game
        /// </summary>
        public static ContentManager ContentManager { get { return Shared.Game.Content; } }

        #endregion

        #region Static

        /// <summary>
        /// Use as a shortcut for load any assets
        /// </summary>
        /// <typeparam name="T">Typeof asset</typeparam>
        /// <param name="assetName">Asset name</param>
        public static T Load<T>(string assetName) 
        {
            return shared.Game.Content.Load<T>(assetName);
        }
        
        #endregion

        private Coco(Game game) : base(game)
        {
            
        }

        protected override void LoadContent()
        {
            Game.Components.Add(Scheduler.Shared);
            Game.Components.Add(ControllerManager.Shared);
            Game.Components.Add(AudioManager.Shared);
            ControllerManager.Shared.UpdateOrder = -10;
        }

        protected override void UnloadContent()
        {
            Game.Components.Remove(Scheduler.Shared);
            Game.Components.Remove(ControllerManager.Shared);
            Game.Components.Remove(AudioManager.Shared);
        }

        readonly Stack<Scene> scenes = new Stack<Scene>();

        void EnterScene(Scene scene) 
        {
            if (scene == null) return;
            if (!scene.IsRunning)
            {
                scene.DoStartScene();
            }
            Debug.WriteLine(">>> Enter Scene: {0}", scene);
        }

        void QuitScene(Scene scene) 
        {
            if (scene == null) return;
            if (scene.IsRunning)
            {
                scene.DoEndScene();
            }
            Debug.WriteLine(">>> Quit Scene: {0}", scene);
        }

        Scene activeScene;
        /// <summary>
        /// Return currently rununing scene
        /// </summary>
        public Scene ActiveScene
        {
            get { return activeScene; }
            private set 
            {
                if (activeScene == value) return;
                QuitScene(activeScene);
                activeScene = value;
                EnterScene(activeScene);
            }
        }

        /// <summary>
        /// Push the actual scene into the stack of scenes and activate other
        /// </summary>
        /// <param name="scene">Scene to activate</param>
        public void PushScene(Scene scene)
        {
            scenes.Push(scene);
            ActiveScene = scene;
        }

        /// <summary>
        /// Pops one scene from the stack and activate it
        /// </summary>
        public void PopScene()
        {
            ActiveScene = scenes.Pop();
        }

        /// <summary>
        /// Clear the stack and set one scene as active
        /// </summary>
        /// <param name="scene"></param>
        public void SetScene(Scene scene)
        {
            scenes.Clear();
            ActiveTransition = null;
            ActiveScene = scene;
        }

        Transition activeTransition;

        /// <summary>
        /// Return the active transition if one is running
        /// </summary>
        public Transition ActiveTransition
        {
            get { return activeTransition; }
            private set 
            {
                UnPlugTransition(activeTransition);
                activeTransition = value;
                PlugTransition(activeTransition);
            }
        }

        void PlugTransition(Transition transition) 
        {
            if (transition == null) return;
            transition.EndTransition += new EventHandler(transition_EndTransition);
            transition.EnterScene += new EventHandler<CustomEventArgs<Scene>>(transition_PlugScene);
            transition.ExitScene += new EventHandler<CustomEventArgs<Scene>>(transition_UnplugScene);
            transition.Initialize();
        }
        
        void UnPlugTransition(Transition transition)
        {
            if (transition == null) return;
            transition.EndTransition -= new EventHandler(transition_EndTransition);
            transition.EnterScene -= new EventHandler<CustomEventArgs<Scene>>(transition_PlugScene);
            transition.ExitScene -= new EventHandler<CustomEventArgs<Scene>>(transition_UnplugScene);
        }

        void transition_PlugScene(object sender, CustomEventArgs<Scene> e)
        {
            EnterScene(e.Value);
        }

        void transition_UnplugScene(object sender, CustomEventArgs<Scene> e)
        {
            QuitScene(e.Value);
        }

        void transition_EndTransition(object sender, EventArgs e)
        {
            if (scenes.Count == 0)
                SetScene(activeTransition.TargetScene);
            else
                PushScene(activeTransition.TargetScene);
            ActiveTransition = null;
        }

        /// <summary>
        /// Run one transition
        /// </summary>
        /// <param name="transition">Transition to run</param>
        public void RunTransition(Transition transition) 
        {
            ActiveTransition = transition;            
        }

        public override void Draw(GameTime gameTime)
        {
            if (ActiveTransition == null)
            {
                if (ActiveScene != null)
                {
                    WorldMatrix.Shared.Begin();
                    ActiveScene.Draw(gameTime);
                    WorldMatrix.Shared.End();
                }
            }
            else 
            {
                ActiveTransition.Draw(gameTime);
            }
        }

        public override void Update(GameTime gameTime)
        {
            if (ActiveScene != null)
            {
                ActiveScene.Update(gameTime);
            }
            if (ActiveTransition != null)
            {
                ActiveTransition.Update(gameTime);
            }
        }

    }
}
